Goroutines
-
Concurrency vs Parallelism :
-
Goroutines provide concurrency (logical simultaneous execution)
-
Parallel execution requires multiple OS threads
-
-
Default Behavior :
-
By default, Go uses GOMAXPROCS=1, meaning goroutines run on a single OS thread (not parallel)
-
They’re multiplexed onto this thread via cooperative scheduling
-
-
Parallel Execution :
-
When GOMAXPROCS > 1 (set to number of CPU cores by default since Go 1.5)
-
The Go scheduler can distribute goroutines across multiple OS threads
-
These threads can then run in parallel on multiple CPU cores
-
-
Important Notes :
-
Even with GOMAXPROCS>1, parallelism isn’t guaranteed - it depends on:
-
Available CPU cores
-
Workload characteristics
-
Scheduler decisions
-
-
Goroutines are much lighter than OS threads (can have millions)
-
-
To check/change GOMAXPROCS:
fmt.Println(runtime.GOMAXPROCS(0)) // Get current value runtime.GOMAXPROCS(4) // Set to 4 threads
How Goroutines Resemble a Job System (When
GOMAXPROCS > 1
)
-
Similarities :
-
Worker Pool Analogy :
-
Each OS thread (
GOMAXPROCS) acts like a worker in a thread pool -
The Go scheduler assigns goroutines (jobs) to these workers
-
-
Work-Stealing Behavior :
-
If one OS thread (worker) runs out of goroutines, it can "steal" goroutines from another thread's queue
-
Similar to how modern job systems optimize load balancing
-
-
Task Granularity :
-
Goroutines are like small, independent units of work (tasks/jobs)
-
The scheduler manages their distribution across workers
-
-
-
Differences :
-
M:N Scheduling :
-
Go uses M goroutines mapped to N OS threads (where M ≫ N)
-
More efficient than 1:1 thread-per-task systems
-
-
Cooperative Scheduling :
-
Goroutines yield control at specific points (channel ops, syscalls, etc.)
-
Unlike preemptive OS thread scheduling
-
-
Automatic Management :
-
No manual "submit job" API needed - just
go func() -
Scheduler handles balancing automatically
-
-
-
What You Might Lose with Go :
-
Precise Control :
-
Can’t pin goroutines to specific cores
-
Limited influence over scheduler decisions
-
-
GC Pauses :
-
Though usually sub-millisecond, can affect some low-latency apps
-
-
-
-
Goroutines and Channels.
-
-
-
Goroutines are not parallel by default, they are concurrent.
-
package main
import (
"fmt"
"log"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
// Check HTTP method
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Get a "name" parameter from URL (ex: /hello?name=John)
name := r.URL.Query().Get("name")
if name == "" {
name = "world"
}
// Respond with a personalized message
message := fmt.Sprintf("Hello, %s!", name)
w.WriteHeader(http.StatusOK)
w.Write([]byte(message))
}
func main() {
// Set up the /hello route
http.HandleFunc("/hello", helloHandler)
// Start the server on port 8080
fmt.Println("Server running at http://localhost:8080")
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatalf("Error starting server: %v", err)
}
}